// xmlparser.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "XML.h"
#include <iostream>
#include <string>
#include <sstream>
#include <stdio.h>
using namespace std;

static int gPass = 0;
static int gFail = 0;

bool XT(const char* teststring, const char* expected, const char* found, bool noEcho = false)
{
	bool pass = !strcmp(expected, found);
	if (pass)
		printf("[pass]");
	else
		printf("[fail]");

	if (noEcho)
		printf("%s\n", teststring);
	else 
		printf("%s [%s][%s]\n", teststring, expected, found);
	if (pass)
		++gPass;
	else
		++gFail;

	return pass;
}


bool XT(const char* teststring, int expected, int found, bool noEcho = false)
{
	bool pass = (expected == found);
	if (pass)
		printf("[pass]");
	else
		printf("[fail]");

	if (noEcho)
		printf("%s\n", teststring);
	else 
		printf("%s [%s][%s]\n", teststring, expected, found);
	if (pass)
		++gPass;
	else
		++gFail;

	return pass;
}


int _tmain(int argc, _TCHAR* argv[])
{

	const char* demoStart =
		"<?xml version=\"1.0\"  standalone='no' >\n"
		"<!-- Our to do list data -->"
		"<ToDo>\n"
		"<!-- Do I need a secure PDA? -->\n"
		"<Item priority=\"1\" distance='close'> Go to the <bold>Toy store!</bold></Item>"
		"<Item priority=\"2\" distance='none'> Do bills   </Item>"
		"<Item priority=\"2\" distance='far &amp; back'> Look for Evil Dinosaurs! </Item>"
		"</ToDo>";

	const char* demoEnd =
		"<?xml version=\"1.0\" standalone=\"no\" ?>"
		"<!-- Our to do list data -->"
		"<ToDo>"
		"<!-- Do I need a secure PDA? -->"
		"<Item priority=\"2\" distance=\"close\">Go to the"
		"<bold>Toy store!"
		"</bold>"
		"</Item>"
		"<Item priority=\"1\" distance=\"far\">Talk to:"
		"<Meeting where=\"School\">"
		"<Attendee name=\"Marple\" position=\"teacher\" />"
		"<Attendee name=\"Voel\" position=\"counselor\" />"
		"</Meeting>"
		"<Meeting where=\"Lunch\" />"
		"</Item>"
		"<Item priority=\"2\" distance=\"here\">Do bills"
		"</Item>"
		"</ToDo>";

	{
		XmlDocument doc("demotest.xml");
		doc.Parse(demoStart);

		if ( doc.Error() )
		{
			printf( "Error in %s: %s\n", doc.Value(), doc.ErrorDesc() );
			exit( 1 );
		}
		doc.SaveFile();
	}

	XmlDocument doc("demotest.xml");
	bool loadOkay = doc.LoadFile();
	
	if ( !loadOkay )
	{
		printf( "Could not load test file 'demotest.xml'. Error='%s'. Exiting.\n", doc.ErrorDesc() );
		exit( 1 );
	}

	printf( "** Demo doc read from disk: ** \n\n" );
	printf( "** Printing via doc.Print **\n" );
	doc.Print( stdout );

	{// TODO: Visitor

	}

	{
		printf( "** Printing via operator<< **\n" );
		std::cout << doc;
	}

	XmlNode* node = 0;
	XmlElement* todoElement = 0;
	XmlElement* itemElement = 0;

	node = doc.FirstChild( "ToDo" );
	assert( node );
	todoElement = node->ToElement();
	assert( todoElement  );

	node = todoElement->FirstChildElement();	// This skips the "PDA" comment.
	assert( node );
	itemElement = node->ToElement();
	assert( itemElement  );
	itemElement->SetAttribute( "priority", 2 );

	itemElement = itemElement->NextSiblingElement();
	assert( itemElement );
	itemElement->SetAttribute( "distance", "here" );

	itemElement = itemElement->NextSiblingElement();
	todoElement->RemoveChild( itemElement );

	itemElement = 0;

	XmlElement item("item");
	item.SetAttribute("priority", "1");
	item.SetAttribute("distance", "far");

	XmlText text("Talk to:");

	XmlElement meeting1( "Meeting" );
	meeting1.SetAttribute( "where", "School" );

	XmlElement meeting2( "Meeting" );
	meeting2.SetAttribute( "where", "Lunch" );

	XmlElement attendee1( "Attendee" );
	attendee1.SetAttribute( "name", "Marple" );
	attendee1.SetAttribute( "position", "teacher" );

	XmlElement attendee2( "Attendee" );
	attendee2.SetAttribute( "name", "Voel" );
	attendee2.SetAttribute( "position", "counselor" );

	// Assemble the nodes we've created:
	meeting1.InsertEndChild( attendee1 );
	meeting1.InsertEndChild( attendee2 );

	item.InsertEndChild( text );
	item.InsertEndChild( meeting1 );
	item.InsertEndChild( meeting2 );

	node = todoElement->FirstChild( "Item" );
	assert( node );
	itemElement = node->ToElement();
	assert( itemElement );

	todoElement->InsertAfterChild( itemElement, item );

	printf( "\n** Demo doc processed: ** \n\n" );
	doc.Print( stdout );

	printf( "** Demo doc processed to stream: ** \n\n" );
	cout << doc << endl << endl;

	int count = 0;
	XmlElement*	element;

	cout << "** Basic structure. **\n";
	ostringstream outputStream( ostringstream::out );
	outputStream << doc;
	XT( "Output stream correct.",	string( demoEnd ).c_str(),
		outputStream.str().c_str(), true );

	node = doc.RootElement();
	assert( node );
	XT( "Root element exists.", true, ( node != 0 && node->ToElement() ) );
	XT ( "Root element value is 'ToDo'.", "ToDo",  node->Value());

	node = node->FirstChild();
	XT( "First child exists & is a comment.", true, ( node != 0 && node->ToComment() ) );
	node = node->NextSibling();
	XT( "Sibling element exists & is an element.", true, ( node != 0 && node->ToElement() ) );
	XT ( "Value is 'Item'.", "Item", node->Value() );

	node = node->FirstChild();
	XT ( "First child exists.", true, ( node != 0 && node->ToText() ) );
	XT ( "Value is 'Go to the'.", "Go to the", node->Value() );

	printf ("\n** Iterators. **\n");

	// Walk all the top level nodes of the document.
	count = 0;
	for( node = doc.FirstChild();
		node;
		node = node->NextSibling() )
	{
		count++;
	}
	XT( "Top level nodes, using First / Next.", 3, count );

	count = 0;
	for( node = doc.LastChild();
		node;
		node = node->PreviousSibling() )
	{
		count++;
	}
	XT( "Top level nodes, using Last / Previous.", 3, count );

	// Walk all the top level nodes of the document,
	// using a different syntax.
	count = 0;
	for( node = doc.IterateChildren( 0 );
		node;
		node = doc.IterateChildren( node ) )
	{
		count++;
	}
	XT( "Top level nodes, using IterateChildren.", 3, count );

	// Walk all the elements in a node.
	count = 0;
	for( element = todoElement->FirstChildElement();
		element;
		element = element->NextSiblingElement() )
	{
		count++;
	}
	XT( "Children of the 'ToDo' element, using First / Next.",
		3, count );

	// Walk all the elements in a node by value.
	count = 0;
	for( node = todoElement->FirstChild( "Item" );
		node;
		node = node->NextSibling( "Item" ) )
	{
		count++;
	}
	XT( "'Item' children of the 'ToDo' element, using First/Next.", 3, count );

	count = 0;
	for( node = todoElement->LastChild( "Item" );
		node;
		node = node->PreviousSibling( "Item" ) )
	{
		count++;
	}
	XT( "'Item' children of the 'ToDo' element, using Last/Previous.", 3, count );

	{
		cout << "\n** Parsing. **\n";
		istringstream parse0( "<Element0 attribute0='foo0' attribute1= noquotes attribute2 = '&gt;' />" );
		XmlElement element0( "default" );
		parse0 >> element0;

		XT ( "Element parsed, value is 'Element0'.", "Element0", element0.Value() );
		XT ( "Reads attribute 'attribute0=\"foo0\"'.", "foo0", element0.Attribute( "attribute0" ));
		XT ( "Reads incorrectly formatted 'attribute1=noquotes'.", "noquotes", element0.Attribute( "attribute1" ) );
		XT ( "Read attribute with entity value '>'.", ">", element0.Attribute( "attribute2" ) );
	}
	{
		const char* error =	"<?xml version=\"1.0\" standalone=\"no\" ?>\n"
			"<passages count=\"006\" formatversion=\"20020620\">\n"
			"    <wrong error>\n"
			"</passages>";

		XmlDocument docTest;
		docTest.Parse( error );
		XT( "Error row", docTest.ErrorRow(), 3 );
		XT( "Error column", docTest.ErrorCol(), 17 );
		//printf( "error=%d id='%s' row %d col%d\n", (int) doc.Error(), doc.ErrorDesc(), doc.ErrorRow()+1, doc.ErrorCol() + 1 );

	}

	{
		//////////////////////////////////////////////////////
		cout << "\n** Streaming. **\n";

		// Round trip check: stream in, then stream back out to verify. The stream
		// out has already been checked, above. We use the output

		istringstream inputStringStream( outputStream.str() );
		XmlDocument document0;

		inputStringStream >> document0;

		ostringstream outputStream0( ostringstream::out );
		outputStream0 << document0;

		XT( "Stream round trip correct.",	string( demoEnd ).c_str(), 
			outputStream0.str().c_str(), true );

		std::string str;
		str << document0;

		XT( "String printing correct.", string( demoEnd ).c_str(), 
			str.c_str(), true );
	}
	return 0;

	{
		const char* str = "<doc attr0='1' attr1='2.0' attr2='foo' />";

		XmlDocument doc;
		doc.Parse( str );

		XmlElement* ele = doc.FirstChildElement();

		int iVal, result;
		double dVal;

		result = ele->QueryDoubleAttribute( "attr0", &dVal );
		XT( "Query attribute: int as double", result, 0 );
		XT( "Query attribute: int as double", (int)dVal, 1 );
		result = ele->QueryDoubleAttribute( "attr1", &dVal );
		XT( "Query attribute: double as double", (int)dVal, 2 );
		result = ele->QueryIntAttribute( "attr1", &iVal );
		XT( "Query attribute: double as int", result, 0 );
		XT( "Query attribute: double as int", iVal, 2 );
		result = ele->QueryIntAttribute( "attr2", &iVal );
		XT( "Query attribute: not a number", result, 2 );
		result = ele->QueryIntAttribute( "bar", &iVal );
		XT( "Query attribute: does not exist", result, 1 );
	}

	/*  NOT HANDLE
{
		const char* str =	"\t<?xml version=\"1.0\" standalone=\"no\" ?>\t<room doors='2'>\n"
			"</room>";

		XmlDocument doc;
		doc.SetTabSize( 8 );
		doc.Parse( str );

		TiXmlHandle docHandle( &doc );
		TiXmlHandle roomHandle = docHandle.FirstChildElement( "room" );

		assert( docHandle.Node() );
		assert( roomHandle.Element() );

		TiXmlElement* room = roomHandle.Element();
		assert( room );
		TiXmlAttribute* doors = room->FirstAttribute();
		assert( doors );

		XmlTest( "Location tracking: Tab 8: room row", room->Row(), 1 );
		XmlTest( "Location tracking: Tab 8: room col", room->Column(), 49 );
		XmlTest( "Location tracking: Tab 8: doors row", doors->Row(), 1 );
		XmlTest( "Location tracking: Tab 8: doors col", doors->Column(), 55 );
	}

	{
	const char* str =	"\t<?xml version=\"1.0\" standalone=\"no\" ?>\t<room doors='2'>\n"
	"  <!-- Silly example -->\n"
	"    <door wall='north'>A great door!</door>\n"
	"\t<door wall='east'/>"
	"</room>";

	TiXmlDocument doc;
	doc.Parse( str );

	TiXmlHandle docHandle( &doc );
	TiXmlHandle roomHandle = docHandle.FirstChildElement( "room" );
	TiXmlHandle commentHandle = docHandle.FirstChildElement( "room" ).FirstChild();
	TiXmlHandle textHandle = docHandle.FirstChildElement( "room" ).ChildElement( "door", 0 ).FirstChild();
	TiXmlHandle door0Handle = docHandle.FirstChildElement( "room" ).ChildElement( 0 );
	TiXmlHandle door1Handle = docHandle.FirstChildElement( "room" ).ChildElement( 1 );

	assert( docHandle.Node() );
	assert( roomHandle.Element() );
	assert( commentHandle.Node() );
	assert( textHandle.Text() );
	assert( door0Handle.Element() );
	assert( door1Handle.Element() );

	TiXmlDeclaration* declaration = doc.FirstChild()->ToDeclaration();
	assert( declaration );
	TiXmlElement* room = roomHandle.Element();
	assert( room );
	TiXmlAttribute* doors = room->FirstAttribute();
	assert( doors );
	TiXmlText* text = textHandle.Text();
	TiXmlComment* comment = commentHandle.Node()->ToComment();
	assert( comment );
	TiXmlElement* door0 = door0Handle.Element();
	TiXmlElement* door1 = door1Handle.Element();

	XmlTest( "Location tracking: Declaration row", declaration->Row(), 1 );
	XmlTest( "Location tracking: Declaration col", declaration->Column(), 5 );
	XmlTest( "Location tracking: room row", room->Row(), 1 );
	XmlTest( "Location tracking: room col", room->Column(), 45 );
	XmlTest( "Location tracking: doors row", doors->Row(), 1 );
	XmlTest( "Location tracking: doors col", doors->Column(), 51 );
	XmlTest( "Location tracking: Comment row", comment->Row(), 2 );
	XmlTest( "Location tracking: Comment col", comment->Column(), 3 );
	XmlTest( "Location tracking: text row", text->Row(), 3 ); 
	XmlTest( "Location tracking: text col", text->Column(), 24 );
	XmlTest( "Location tracking: door0 row", door0->Row(), 3 );
	XmlTest( "Location tracking: door0 col", door0->Column(), 5 );
	XmlTest( "Location tracking: door1 row", door1->Row(), 4 );
	XmlTest( "Location tracking: door1 col", door1->Column(), 5 );
	}
*/

	printf ("\n** UTF-8 **\n");

	/*
	{
	TiXmlDocument doc( "utf8test.xml" );
	doc.LoadFile();
	if ( doc.Error() && doc.ErrorId() == TiXmlBase::TIXML_ERROR_OPENING_FILE ) {
	printf( "WARNING: File 'utf8test.xml' not found.\n"
	"(Are you running the test from the wrong directory?)\n"
	"Could not test UTF-8 functionality.\n" );
	}
	else
	{
	TiXmlHandle docH( &doc );
	// Get the attribute "value" from the "Russian" element and check it.
	TiXmlElement* element = docH.FirstChildElement( "document" ).FirstChildElement( "Russian" ).Element();
	const unsigned char correctValue[] = {	0xd1U, 0x86U, 0xd0U, 0xb5U, 0xd0U, 0xbdU, 0xd0U, 0xbdU, 
	0xd0U, 0xbeU, 0xd1U, 0x81U, 0xd1U, 0x82U, 0xd1U, 0x8cU, 0 };

	XmlTest( "UTF-8: Russian value.", (const char*)correctValue, element->Attribute( "value" ), true );
	XmlTest( "UTF-8: Russian value row.", 4, element->Row() );
	XmlTest( "UTF-8: Russian value column.", 5, element->Column() );

	const unsigned char russianElementName[] = {	0xd0U, 0xa0U, 0xd1U, 0x83U,
	0xd1U, 0x81U, 0xd1U, 0x81U,
	0xd0U, 0xbaU, 0xd0U, 0xb8U,
	0xd0U, 0xb9U, 0 };
	const char russianText[] = "<\xD0\xB8\xD0\xBC\xD0\xB5\xD0\xB5\xD1\x82>";

	TiXmlText* text = docH.FirstChildElement( "document" ).FirstChildElement( (const char*) russianElementName ).Child( 0 ).Text();
	XmlTest( "UTF-8: Browsing russian element name.",
	russianText,
	text->Value(),
	true );
	XmlTest( "UTF-8: Russian element name row.", 7, text->Row() );
	XmlTest( "UTF-8: Russian element name column.", 47, text->Column() );

	TiXmlDeclaration* dec = docH.Child( 0 ).Node()->ToDeclaration();
	XmlTest( "UTF-8: Declaration column.", 1, dec->Column() );
	XmlTest( "UTF-8: Document column.", 1, doc.Column() );

	// Now try for a round trip.
	doc.SaveFile( "utf8testout.xml" );

	// Check the round trip.
	char savedBuf[256];
	char verifyBuf[256];
	int okay = 1;

	FILE* saved  = fopen( "utf8testout.xml", "r" );
	FILE* verify = fopen( "utf8testverify.xml", "r" );
	if ( saved && verify )
	{
	while ( fgets( verifyBuf, 256, verify ) )
	{
	fgets( savedBuf, 256, saved );
	if ( strcmp( verifyBuf, savedBuf ) )
	{
	okay = 0;
	break;
	}
	}
	fclose( saved );
	fclose( verify );
	}
	XmlTest( "UTF-8: Verified multi-language round trip.", 1, okay );

	// On most Western machines, this is an element that contains
	// the word "resume" with the correct accents, in a latin encoding.
	// It will be something else completely on non-wester machines,
	// which is why TinyXml is switching to UTF-8.
	const char latin[] = "<element>r\x82sum\x82</element>";

	TiXmlDocument latinDoc;
	latinDoc.Parse( latin, 0, TIXML_ENCODING_LEGACY );

	text = latinDoc.FirstChildElement()->FirstChild()->ToText();
	XmlTest( "Legacy encoding: Verify text element.", "r\x82sum\x82", text->Value() );
	}
	}
	*/
printf ("\n** Copy and Assignment **\n");
{
	XmlElement element( "foo" );
	element.Parse( "<element name='value' />", 0, XE_UNKNOWN );

	XmlElement elementCopy( element );
	XmlElement elementAssign( "foo" );
	elementAssign.Parse( "<incorrect foo='bar'/>", 0, XE_UNKNOWN );
	elementAssign = element;

	XT( "Copy/Assign: element copy #1.", "element", elementCopy.Value() );
	XT( "Copy/Assign: element copy #2.", "value", elementCopy.Attribute( "name" ) );
	XT( "Copy/Assign: element assign #1.", "element", elementAssign.Value() );
	XT( "Copy/Assign: element assign #2.", "value", elementAssign.Attribute( "name" ) );
	XT( "Copy/Assign: element assign #3.", true, ( 0 == elementAssign.Attribute( "foo" )) );

	XmlComment comment;
	comment.Parse( "<!--comment-->", 0, XE_UNKNOWN );
	XmlComment commentCopy( comment );
	XmlComment commentAssign;
	commentAssign = commentCopy;
	XT( "Copy/Assign: comment copy.", "comment", commentCopy.Value() );
	XT( "Copy/Assign: comment assign.", "comment", commentAssign.Value() );

	XmlUnknown unknown;
	unknown.Parse( "<[unknown]>", 0, XE_UNKNOWN );
	XmlUnknown unknownCopy( unknown );
	XmlUnknown unknownAssign;
	unknownAssign.Parse( "incorrect", 0, XE_UNKNOWN );
	unknownAssign = unknownCopy;
	XT( "Copy/Assign: unknown copy.", "[unknown]", unknownCopy.Value() );
	XT( "Copy/Assign: unknown assign.", "[unknown]", unknownAssign.Value() );

	XmlText text( "TextNode" );
	XmlText textCopy( text );
	XmlText textAssign( "incorrect" );
	textAssign = text;
	XT( "Copy/Assign: text copy.", "TextNode", textCopy.Value() );
	XT( "Copy/Assign: text assign.", "TextNode", textAssign.Value() );

	XmlDeclaration dec;
	dec.Parse( "<?xml version='1.0' encoding='UTF-8'?>", 0, XE_UNKNOWN );
	XmlDeclaration decCopy( dec );
	XmlDeclaration decAssign;
	decAssign = dec;

	XT( "Copy/Assign: declaration copy.", "UTF-8", decCopy.Encoding() );
	XT( "Copy/Assign: text assign.", "UTF-8", decAssign.Encoding() );

	XmlDocument doc;
	elementCopy.InsertEndChild( textCopy );
	doc.InsertEndChild( decAssign );
	doc.InsertEndChild( elementCopy );
	doc.InsertEndChild( unknownAssign );

	XmlDocument docCopy( doc );
	XmlDocument docAssign;
	docAssign = docCopy;


	std::string original, copy, assign;
	original << doc;
	copy << docCopy;
	assign << docAssign;
	XT( "Copy/Assign: document copy.", original.c_str(), copy.c_str(), true );
	XT( "Copy/Assign: document assign.", original.c_str(), assign.c_str(), true );


}

{
	const char* str = "<foo>This is text</foo>";
	XmlDocument doc;
	doc.Parse( str );
	const XmlElement* element = doc.RootElement();

	XT( "GetText() normal use.", "This is text", element->GetText() );

	str = "<foo><b>This is text</b></foo>";
	doc.Clear();
	doc.Parse( str );
	element = doc.RootElement();

	XT( "GetText() contained element.", element->GetText() == 0, true );

	str = "<foo>This is <b>text</b></foo>";
	doc.Clear();
	//XmlBase::SetCondenseWhiteSpace( false );
	doc.Parse( str );
	//TiXmlBase::SetCondenseWhiteSpace( true );
	element = doc.RootElement();

	XT( "GetText() partial.", "This is ", element->GetText() );
}
}

